"use client"

import * as React from "react"
import * as RechartsPrimitive from "recharts"

import { ny } from "~/lib/utils"

// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const

export type ChartConfig = {
   [k in string]: {
      label?: React.ReactNode
      icon?: React.ComponentType
   } & (
      | { color?: string; theme?: never }
      | { color?: never; theme: Record<keyof typeof THEMES, string> }
   )
}

interface ChartContextProps {
   config: ChartConfig
}

const ChartContext = React.createContext<ChartContextProps | null>(null)

function useChart() {
   const context = React.useContext(ChartContext)

   if (!context) {
      throw new Error("useChart must be used within a <ChartContainer />")
   }

   return context
}

const ChartContainer = React.forwardRef<
   HTMLDivElement,
   React.ComponentProps<"div"> & {
      config: ChartConfig
      children: React.ComponentProps<
         typeof RechartsPrimitive.ResponsiveContainer
      >["children"]
   }
>(({ id, className, children, config, ...props }, ref) => {
   const uniqueId = React.useId()
   const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`

   return (
      <ChartContext.Provider value={{ config }}>
         <div
            data-chart={chartId}
            ref={ref}
            className={ny(
               "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line-line]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
               className,
            )}
            {...props}
         >
            <ChartStyle id={chartId} config={config} />
            <RechartsPrimitive.ResponsiveContainer>
               {children}
            </RechartsPrimitive.ResponsiveContainer>
         </div>
      </ChartContext.Provider>
   )
})
ChartContainer.displayName = "Chart"

function ChartStyle({ id, config }: { id: string; config: ChartConfig }) {
   const colorConfig = Object.entries(config).filter(
      ([_, config]) => config.theme || config.color,
   )

   if (!colorConfig.length) {
      return null
   }

   return (
      <style
         dangerouslySetInnerHTML={{
            __html: Object.entries(THEMES).map(
               ([theme, prefix]) => `
${prefix} [data-chart=${id}] {
${colorConfig
   .map(([key, itemConfig]) => {
      const color =
         itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
         itemConfig.color
      return color ? `  --color-${key}: ${color};` : null
   })
   .join("\n")}
}
`,
            ),
         }}
      />
   )
}

const ChartTooltip = RechartsPrimitive.Tooltip

const ChartTooltipContent = React.forwardRef<
   HTMLDivElement,
   React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
      React.ComponentProps<"div"> & {
         hideLabel?: boolean
         hideIndicator?: boolean
         indicator?: "line" | "dot" | "dashed"
         nameKey?: string
         labelKey?: string
      }
>(
   (
      {
         active,
         payload,
         className,
         indicator = "dot",
         hideLabel = false,
         hideIndicator = false,
         label,
         labelFormatter,
         labelClassName,
         formatter,
         color,
         nameKey,
         labelKey,
      },
      ref,
   ) => {
      const { config } = useChart()

      const tooltipLabel = React.useMemo(() => {
         if (hideLabel || !payload?.length) {
            return null
         }

         const [item] = payload
         const key = `${labelKey || item?.dataKey || item?.name || "value"}`
         const itemConfig = getPayloadConfigFromPayload(config, item, key)
         const value =
            !labelKey && typeof label === "string"
               ? config[label as keyof typeof config]?.label || label
               : itemConfig?.label

         if (labelFormatter) {
            return (
               <div className={ny("font-medium", labelClassName)}>
                  {labelFormatter(value, payload)}
               </div>
            )
         }

         if (!value) {
            return null
         }

         return <div className={ny("font-medium", labelClassName)}>{value}</div>
      }, [
         label,
         labelFormatter,
         payload,
         hideLabel,
         labelClassName,
         config,
         labelKey,
      ])

      if (!active || !payload?.length) {
         return null
      }

      const nestLabel = payload.length === 1 && indicator !== "dot"

      return (
         <div
            ref={ref}
            className={ny(
               "border-border/50 bg-background grid min-w-32 items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
               className,
            )}
         >
            {!nestLabel ? tooltipLabel : null}
            <div className="grid gap-1.5">
               {payload.map((item, index) => {
                  const key = `${nameKey || item.name || item.dataKey || "value"}`
                  const itemConfig = getPayloadConfigFromPayload(
                     config,
                     item,
                     key,
                  )
                  const indicatorColor =
                     color || item.payload.fill || item.color

                  return (
                     <div
                        key={item.dataKey}
                        className={ny(
                           "[&>svg]:text-muted-foreground flex w-full items-stretch gap-2 [&>svg]:size-2.5",
                           indicator === "dot" && "items-center",
                        )}
                     >
                        {formatter && item.value && item.name ? (
                           formatter(
                              item.value,
                              item.name,
                              item,
                              index,
                              item.payload,
                           )
                        ) : (
                           <>
                              {itemConfig?.icon ? (
                                 <itemConfig.icon />
                              ) : (
                                 !hideIndicator && (
                                    <div
                                       className={ny(
                                          "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
                                          {
                                             "h-2.5 w-2.5": indicator === "dot",
                                             "w-1": indicator === "line",
                                             "w-0 border-[1.5px] border-dashed bg-transparent":
                                                indicator === "dashed",
                                             "my-0.5":
                                                nestLabel &&
                                                indicator === "dashed",
                                          },
                                       )}
                                       style={
                                          {
                                             "--color-bg": indicatorColor,
                                             "--color-border": indicatorColor,
                                          } as React.CSSProperties
                                       }
                                    />
                                 )
                              )}
                              <div
                                 className={ny(
                                    "flex flex-1 justify-between leading-none",
                                    nestLabel ? "items-end" : "items-center",
                                 )}
                              >
                                 <div className="grid gap-1.5">
                                    {nestLabel ? tooltipLabel : null}
                                    <span className="text-muted-foreground">
                                       {itemConfig?.label || item.name}
                                    </span>
                                 </div>
                                 {item.value !== undefined && (
                                    <span className="text-foreground font-mono font-medium tabular-nums">
                                       {item.value.toLocaleString()}
                                    </span>
                                 )}
                              </div>
                           </>
                        )}
                     </div>
                  )
               })}
            </div>
         </div>
      )
   },
)
ChartTooltipContent.displayName = "ChartTooltip"

const ChartLegend = RechartsPrimitive.Legend

const ChartLegendContent = React.forwardRef<
   HTMLDivElement,
   React.ComponentProps<"div"> &
      Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
         hideIcon?: boolean
         nameKey?: string
      }
>(
   (
      {
         className,
         hideIcon = false,
         payload,
         verticalAlign = "bottom",
         nameKey,
      },
      ref,
   ) => {
      const { config } = useChart()

      if (!payload?.length) {
         return null
      }

      return (
         <div
            ref={ref}
            className={ny(
               "flex items-center justify-center gap-4",
               verticalAlign === "top" ? "pb-3" : "pt-3",
               className,
            )}
         >
            {payload.map((item) => {
               const key = `${nameKey || item.dataKey || "value"}`
               const itemConfig = getPayloadConfigFromPayload(config, item, key)

               return (
                  <div
                     key={item.value}
                     className={ny(
                        "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:size-3",
                     )}
                  >
                     {itemConfig?.icon && !hideIcon ? (
                        <itemConfig.icon />
                     ) : (
                        <div
                           className="size-2 shrink-0 rounded-[2px]"
                           style={{
                              backgroundColor: item.color,
                           }}
                        />
                     )}
                     {itemConfig?.label}
                  </div>
               )
            })}
         </div>
      )
   },
)
ChartLegendContent.displayName = "ChartLegend"

// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(
   config: ChartConfig,
   payload: unknown,
   key: string,
) {
   if (typeof payload !== "object" || payload === null) {
      return undefined
   }

   const payloadPayload =
      "payload" in payload &&
      typeof payload.payload === "object" &&
      payload.payload !== null
         ? payload.payload
         : undefined

   let configLabelKey: string = key

   if (
      key in payload &&
      typeof payload[key as keyof typeof payload] === "string"
   ) {
      configLabelKey = payload[key as keyof typeof payload] as string
   } else if (
      payloadPayload &&
      key in payloadPayload &&
      typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
   ) {
      configLabelKey = payloadPayload[
         key as keyof typeof payloadPayload
      ] as string
   }

   return configLabelKey in config
      ? config[configLabelKey]
      : config[key as keyof typeof config]
}

export {
   ChartContainer,
   ChartTooltip,
   ChartTooltipContent,
   ChartLegend,
   ChartLegendContent,
   ChartStyle,
}
